﻿using CommonBehaviors.Actions;
//Created: 3/3/2013 @ 4:30pm by Dagradt
//Last changed date: 3/7/2013 @ 9:44pm by Dagradt
using Styx;
using Styx.Common;
using Styx.CommonBot;
using Styx.CommonBot.Routines;
using Styx.TreeSharp;
using Styx.WoWInternals;
using Styx.WoWInternals.WoWObjects;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Timers;
using Action = Styx.TreeSharp.Action;

namespace Finesse
{
    public class Main : CombatRoutine
    {
        public override string Name { get { return "Finesse"; } }
        public override WoWClass Class { get { return WoWClass.Rogue; } }
        LocalPlayer Me { get { return StyxWoW.Me; } }

        private Composite _precombatbuffs, _combatbuffs, _heal, _combat;
        public override Composite PreCombatBuffBehavior { get { return _precombatbuffs; } }
        public override Composite CombatBuffBehavior { get { return _combatbuffs; } }
        public override Composite HealBehavior { get { return _heal; } }
        public override Composite CombatBehavior { get { return _combat; } }

        #region Initialize
        //System.Timers.Timer Heartbeat;
        public override void Initialize()
        {
            _precombatbuffs = createPreCombatBuffs();
            _combatbuffs = createCombatBuffs();
            _heal = createHeal();
            _combat = createCombat();
            //Heartbeat = new System.Timers.Timer(200);
            //Heartbeat.Elapsed += new ElapsedEventHandler(Beat);
            //Heartbeat.Enabled = true;
        }
        #endregion

        #region Pre-Combat Buffs
        Composite createPreCombatBuffs()
        {
            return
                new Decorator(ret => !Me.Mounted,
                    new PrioritySelector(
                        //0 	0.00 	flask,type=spring_blossoms
                        //1 	0.00 	food,type=sea_mist_rice_noodles
                        //apply_poison,lethal=deadly
                        applyDeadlyPoison(),
                        //4 	0.00 	virmens_bite_potion
                        //stealth
                        Cast("Stealth", ret => !Stealth),
                        //premeditation
                        Cast("Premeditation", ret => positionOmni && targetDistance(Me.CurrentTarget) <= 30 * 30 && Stealth && !spellOnCooldown("Premeditation"), ignoreCanCast: true),
                        //slice_and_dice
                        Cast("Slice and Dice", ret => positionOmni),
                        new Decorator(ret => Open && targetDistance(Me.CurrentTarget) <= 5 * 5, Opener())
                        ));
        }
        #endregion

        #region Combat Buffs
        Composite createCombatBuffs()
        {
            return
                new PrioritySelector(
                    );
        }
        #endregion

        #region Heal
        Composite createHeal()
        {
            return
                new PrioritySelector(
                    );
        }
        #endregion

        #region Combat
        Composite createCombat()
        {
            return
                new PrioritySelector(
                    new Decorator(ret => openerUsed(),
                        new Action(delegate
                            {
                                Lua.DoString("RunMacroText('/castsequence reset=8 Ambush, Eviscerate, Cheap Shot, Ambush, Eviscerate');");
                            })),
                    new Decorator(ret => !openerUsed(),
                        new PrioritySelector(
                //8 	1.00 	virmens_bite_potion,if=buff.bloodlust.react|target.time_to_die<40
                    //preparation,if=talent.preparation.enabled&!buff.vanish.up&cooldown.vanish.remains>60
                    //Cast("Preparation", ret => positionOmni && !auraExists("Vanish", Me) && spellCooldownLeft("Vanish").TotalSeconds > 60), <~ Fucking shit up
                //A 	5.18 	auto_attack
                //B 	0.00 	kick
                    //shadow_blades
                    Cast("Shadow Blades", ret => positionOmni && !spellOnCooldown("Shadow Blades"), ignoreCanCast: true),
                //D 	6.65 	pool_resource,for_next=1,extra_amount=75
                    //shadow_dance,if=energy>=75&buff.stealthed.down&!target.debuff.find_weakness.up
                    Cast("Shadow Dance", ret => positionOmni && Energy >= 75 && !Stealth && !auraExists("Find Weakness", Me.CurrentTarget) && !spellOnCooldown("Shadow Dance"), ignoreCanCast: true),
                //F 	7.77 	use_item,name=gloves_of_the_thousandfold_blades,if=buff.shadow_dance.up
                //G 	2.90 	berserking,if=buff.shadow_dance.up
                //H 	2.92 	pool_resource,for_next=1,extra_amount=30
                //I 	4.18 	vanish,if=time>10&energy>=45&energy<=75&combo_points<=3&!buff.shadow_dance.up&!buff.master_of_subtlety.up&!target.debuff.find_weakness.up
                //J 	9.20 	premeditation,if=(combo_points<=3&cooldown.honor_among_thieves.remains>1.75)|combo_points<=2
                    //ambush,if=combo_points<=5&anticipation_charges=0
                    Cast("Ambush", ret => positionBehind && targetDistance(Me.CurrentTarget) <= 30 * 30 && shadowDance && comboPoints <= 5 && auraStackCount("Anticipation") == 0),
                    //slice_and_dice,if=buff.slice_and_dice.remains<3&combo_points=5
                    Cast("Slice and Dice", ret => positionOmni && needSliceAndDice && comboPoints == 5),
                    //rupture,if=combo_points=5&dot.rupture.remains<5
                    Cast("Rupture", ret => positionOmni && targetDistance(Me.CurrentTarget) <= 5 * 5 && targetDistance(Me.CurrentTarget) <= 5 * 5 && comboPoints == 5 && needRupture),
                    //ambush,if=anticipation_charges<3&buff.shadow_dance.remains<=2
                    Cast("Ambush", ret => positionBehind && targetDistance(Me.CurrentTarget) <= 5 * 5 && shadowDance && auraStackCount("Anticipation") < 3 && auraTimeLeft("Shadow Dance", Me) <= 2),
                    //eviscerate,if=combo_points=5
                    Cast("Eviscerate", ret => positionOmni && targetDistance(Me.CurrentTarget) <= 5 * 5 && comboPoints == 5),
                    //hemorrhage,if=combo_points<4&(dot.hemorrhage.remains<4|position_front)
                    Cast("Hemorrhage", ret => positionOmni && targetDistance(Me.CurrentTarget) <= 5 * 5 && comboPoints < 4 && (needHemorrhage || positionFront)),
                    //hemorrhage,if=combo_points<5&energy>80&(dot.hemorrhage.remains<4|position_front)
                    Cast("Hemorrhage", ret => positionOmni && targetDistance(Me.CurrentTarget) <= 5 * 5 && comboPoints < 5 && Energy > 80 && (needHemorrhage || positionFront)),
                //R 	118.61 	backstab,if=combo_points<4&(cooldown.shadow_dance.remains>7|(cooldown.shadow_dance.remains=0&time<=9))
                //S 	14.75 	tricks_of_the_trade
                    //backstab,if=combo_points<5&energy>80&cooldown.shadow_dance.remains>=2
                    Cast("Backstab", ret => positionBehind && targetDistance(Me.CurrentTarget) <= 5 * 5 && comboPoints < 5 && Energy > 80 && spellCooldownLeft("Shadow Dance").TotalSeconds >= 2)
                    )));
        }
        #endregion

        #region Methods

        #region Aura Methods
        public bool auraExists(string Aura, WoWUnit onUnit)
        {
            if (onUnit != null)
            {
                var result = onUnit.GetAuraByName(Aura);
                if (result != null)
                {
                    return true;
                }
            }
            return false;
        }
        public double auraTimeLeft(string Aura, WoWUnit onUnit)
        {
            if (onUnit != null)
            {
                var result = onUnit.GetAuraByName(Aura);
                if (result != null)
                {
                    if (result.TimeLeft.TotalSeconds > 0)
                        return result.TimeLeft.TotalSeconds;
                }
            }
            return 0;
        }
        public uint auraStackCount(string aura)
        {
            var result = Me.GetAuraByName(aura);
            if (result != null)
            {
                if (result.StackCount > 0)
                    return result.StackCount;
            }

            return 0;
        }
        #endregion

        #region Buffs
        public bool needSliceAndDice { get { return auraTimeLeft("Slice and Dice", Me) < 3; } }
        #endregion

        #region Combo Points
        public double comboPoints
        {
            get
            {
                try
                {
                    using (StyxWoW.Memory.AcquireFrame())
                    {
                        return Styx.WoWInternals.Lua.GetReturnVal<int>("return GetComboPoints(\"player\");", 0);
                    }
                }
                catch
                {
                    return 0;
                }
            }
        }
        #endregion

        #region Cooldown
        TimeSpan spellCooldownLeft(string Spell)
        {
            SpellFindResults results;
            if (SpellManager.FindSpell(Spell, out results))
            {
                if (results.Override != null)
                    return results.Override.CooldownTimeLeft;
                return results.Override.CooldownTimeLeft;
            }
            return TimeSpan.MaxValue;
        }
        public bool spellOnCooldown(int Spell)
        {
            SpellFindResults results;
            if (SpellManager.FindSpell(Spell, out results))
                return results.Override != null ? results.Override.Cooldown : results.Original.Cooldown;
            return false;
        }
        #endregion

        #region Energy
        public int Energy
        {
            get
            {
                try
                {
                    using (StyxWoW.Memory.AcquireFrame())
                    {
                        return Styx.WoWInternals.Lua.GetReturnVal<int>("return UnitPower(\"player\");", 0);
                    }
                }
                catch
                {
                    return 0;
                }
            }
        }
        #endregion

        #region Debuffs
        #endregion

        #region Distance Check
        public double targetDistance(WoWUnit onUnit)
        {
            if (onUnit != null)
            {
                var results = onUnit.DistanceSqr;
                if (results != null)
                {
                    return Me.CurrentTarget.DistanceSqr;
                }
            }
            return 0;
        }
        #endregion

        #region DoTs
        public bool needHemorrhage { get { return auraTimeLeft("Hemorrhage", Me.CurrentTarget) < 4; } }
        public bool needRupture { get { return auraTimeLeft("Rupture", Me.CurrentTarget) < 5; } }
        #endregion

        #region Macros
        public bool Manual { get { return Convert.ToBoolean(Lua.GetReturnVal<int>("return Manual and 0 or 1", 0)); } }
        public bool Open { get { return Convert.ToBoolean(Lua.GetReturnVal<int>("return Opener and 0 or 1", 0)); } }
        #endregion

        #region Openers
        public bool openerUsed()
        {
            if (auraExists("Garrote", Me.CurrentTarget) && auraExists("Shadow Dance", Me) && auraExists("Shadow Blades", Me))
                return true;
            return false;
        }
        Composite Opener()
        {
            return
                new Sequence(
                    Cast("Garrote", ret => positionOmni && targetDistance(Me.CurrentTarget) <= 30 * 30 && Stealth),
                    Cast("Shadow Dance", ret => positionOmni && !spellOnCooldown("Shadow Dance"), ignoreCanCast: true),
                    Cast("Shadow Blades", ret => positionOmni && !spellOnCooldown("Shadow Blades"), ignoreCanCast: true),
                    //Use Trinket here
                    Cast("Cloak of Shadows", ret => positionOmni && !spellOnCooldown("Cloak of Shadows"), ignoreCanCast: true)
                    );
        }
        #endregion

        #region Poisons
        Composite applyDeadlyPoison()
        {
            return
                new Decorator(ret => !auraExists("Deadly Poison", Me),
                    new Action(delegate
                        {
                            if (lastSpellCast != "Deadly Poison")
                            {
                                SpellManager.Cast("Deadly Poison");
                                lastSpellCast = "Deadly Poison";
                            }
                        })
                    );

        }
        #endregion

        #region Pool Energy
        #endregion

        #region Pulsed Checks
        //public void Beat(object sender, ElapsedEventArgs e)
        //{
        //}
        #endregion

        #region Spell Casting
        private delegate T Selection<out T>(object context);
        Composite Cast(string Spell, Selection<bool> reqs = null, Selection<WoWUnit> onTarget = null, bool ignoreCanCast = false)
        {
            return
                new Decorator(ret =>
                    {
                        if (reqs != null && !reqs(ret))
                            return false;
                        if (!ignoreCanCast && !SpellManager.CanCast(Spell, onTarget != null ? onTarget(ret) : Me.CurrentTarget))
                            return false;
                        return true;
                    },
                    new Action(ret =>
                        {
                            WoWSpell castingSpell = Me.CastingSpell;
                            if(castingSpell != null && castingSpell.Name != Spell)
                                Lua.DoString("SpellStopCasting()");
                            Logging.Write("Casting: "+Spell);
                            SpellManager.Cast(Spell, (onTarget != null ? onTarget(ret) : null));
                        })
                    );
        }
        //Composite Apply(string Spell, Selection<bool> reqs = null, Selection<WoWUnit> onTarget = null, int msLeft = 0, bool ignoreCanCast = false)
        //{
        //    return
        //        new Decorator(ret =>
        //            {
        //                if (reqs != null && !reqs(ret))
        //                    return false;
        //                WoWUnit Target = onTarget != null ? onTarget(ret) : Me.CurrentTarget;
        //                if (!ignoreCanCast && !SpellManager.CanCast(Spell, Target))
        //                    return false;
        //                if (!Target.HasAura(Spell, 1, true, msLeft))
        //                    return true;
        //                return false;
        //            },
        //            new Action(ret =>
        //                {
        //                    WoWSpell castingSpell = Me.CastingSpell;
        //                    if (castingSpell != null && castingSpell.Name != Spell)
        //                        Lua.DoString("SpellStopCasting()");
        //                    Logging.Write("Casting: " + Spell);
        //                    SpellManager.Cast(Spell, (onTarget != null ? onTarget(ret) : null));
        //                })
        //            );
        //}
        #endregion

        #region Stealth
        public bool shadowDance { get { return auraExists("Shadow Dance", Me); } }
        public bool Stealth { get { return auraExists("Stealth", Me); } }
        #endregion

        #region Unit
        public bool positionBehind { get { return Me.GotTarget && Me.CurrentTarget != null && !Me.CurrentTarget.IsFriendly && Me.CurrentTarget.IsAlive && Me.IsSafelyBehind(Me.CurrentTarget); } }
        public bool positionFront { get { return Me.GotTarget && Me.CurrentTarget != null && !Me.CurrentTarget.IsFriendly && Me.CurrentTarget.IsAlive && !Me.IsSafelyBehind(Me.CurrentTarget); } }
        public bool positionOmni { get { return Me.GotTarget && Me.CurrentTarget != null && !Me.CurrentTarget.IsFriendly && Me.CurrentTarget.IsAlive; } }
        #endregion

        #endregion
    }
}
